/*   The MIT License
 *   
 *   Carina Engine
 *   Copyright (c) 2009 2010 2011 2012 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#ifndef _WORLD_MANAGER_HH
#define _WORLD_MANAGER_HH

#include "carina/common/global.hh"
#include "carina/collision-detection.hh"
#include "carina/scene.hh"
#include "carina/mesh.hh"

namespace Carina
{
//! Contains information about a given model inside the scene.
class Model
{
    MeshPtr             m_Mesh; //!< The mesh associated with this model.
    AABBHierarchyPtr    m_ColH; //!< The collision hierarchy used for collision detection.
public:
    /*! \brief Constructor.
     *  \param mesh     a pointer to the mesh.
     *  \param colh     a pointer to the collision hierarchy.
     */
    Model(const MeshPtr& mesh, const AABBHierarchyPtr& colh);

    //! Returns a pointer to the mesh.
    MeshPtr getMesh();

    //! Returns a pointer to the collision hierarchy.
    AABBHierarchyPtr getBVH();
};

//! The Model pointer type.
typedef shared_ptr<Model> ModelPtr;

//! Contains additional information about a particular model inside the scene.
class ModelInstance
{
    ModelPtr            m_Model;    //!< The original model.
    AABBHierarchy       m_CLCache;  //!< The translated collision detection hierarchy.
public:
    /*! \brief Constructor.
     *  \param mod  a pointer to the common model.
     */
    ModelInstance(const ModelPtr& mod);

    //! Returns a pointer to the current model.
    ModelPtr getModel() { return m_Model; }

    /*! \brief Updates the stuff which are dependent on the actual orientation of this
     *         instance of the model in the particular scene.
     *  \param mat      a reference to the matrix which represents the current position, scale, orientation
     *                  of the model in the scene.
     */
    void update(const Matrix4& mat);

    //! Tests for intersection between two model.
    bool testCollision(const ModelInstance& model_inst) const;

    //! Tests for intersection between this model and a ray.
    bool testIntersection(const Ray& ray) const;
    
    //! Tests for intersection between this model and a line segment.
    bool testIntersection(const Segment& seg) const;
};

//! The Model Instance pointer type.
typedef shared_ptr<ModelInstance> ModelInstancePtr;

class WorldManager;

//! The World Manager pointer type.
typedef shared_ptr<WorldManager> WorldManagerPtr;

//! Contains information about a scene node which contains a model.
class ModelNode: public Node, public Configurable, public enable_shared_from_this<ModelNode>
{
    ModelInstancePtr    m_ModelInst;    //!< A pointer to the instance of the model.
    WorldManagerPtr     m_WorldManager; //!< A pointer to the world manager.
    Matrix4             m_WorldMatrix;  //!< The actual matrix which represents the transformation applied to this node.
public:
    /*! \brief Constructor.
     *  \param wmgr     a pointer to the world manager.
     *  \param model    a pointer to the instance of the model that is going to be attached to this node.
     */
    ModelNode(const WorldManagerPtr& wmgr, const ModelInstancePtr& model);

    //! Destructor.
    virtual ~ModelNode();

    /*! \brief Draws the contents of this node to the screen.
     *  \param scene    a reference to the main scene object.
     */
    virtual void draw(Scene& scene);

    /*! \brief Notifies the node about change of its parent's world matrix.
     *  \param wmat     parent's world matrix.
     */
    virtual void notifyTransform(const Matrix4& wmat);

    //! Returns a pointer to the object used for configuring some options of this none; i.e., pointer to this.
    virtual ConfigurablePtr getConfigurable();
    
    //! Returns a pointer to the object used for configuring some options of this none; i.e., pointer to this.
    virtual ConfigurableConstPtr getConfigurable() const;

    //! Declaration of an empty attribute list.
    CE_NO_ATTRIBUTES(CE_POLYMORPHIC, NullBaseClass)
};

//! The type used for representing a pointer to node which contains a model.
typedef shared_ptr<ModelNode> ModelNodePtr;

//! Maintains information about the available collidable objects in the world.
class WorldManager
{
    //! Data type used for maintaining a list of all available model types.
    typedef std::unordered_map<string, ModelPtr> ModelMap;

    //! List of all model instances in the world.
    DynamicArray<ModelInstancePtr>  m_ModelArray;

    //! List of all available model types.
    ModelMap                        m_ModelCache;
public:
    //! Constructor.
    WorldManager();

    //! Destructor.
    ~WorldManager();

    //! Returns a pointer to the model with the specified name.
    ModelPtr getModel(const string& model_name);

    //! Removes all objects from the world.
    void clear();

    //! Populates the list with all model instances that intersect a particular ray.
    void rayQuery(const Ray& ray, DataVector<ModelInstancePtr>& col);

    //! Populates the list with all model instances that intersect a particular line segment.
    void segmentQuery(const Segment& seg, DataVector<ModelInstancePtr>& col);

    //! Attaches a new instance of a model to the world.
    void attachModelInstance(const ModelInstancePtr& model_inst);

    //! Detaches a particular instance of a model from the world.
    void detachModelInstance(const ModelInstancePtr& model_inst);

    //! Registers a new model with the specified name.
    void registerModel(const string& name, const ModelPtr& model);

    //! Deregisters a model with the specified name.
    void unregisterModel(const string& name);
};

//! The World Manager pointer type.
typedef shared_ptr<WorldManager> WorldManagerPtr;
}

#endif /* _WORLD_MANAGER_HH */